//=============================================================================
//     _   _                               ____                _  _          
//    | | | |  __ _  _ __   _ __   _   _  / ___|  _ __   _ __ (_)| |_   ___ 
//    | |_| | / _` || '_ \ | '_ \ | | | | \___ \ | '_ \ | '__|| || __| / _ \
//    |  _  || (_| || |_) || |_) || |_| |  ___) || |_) || |   | || |_ |  __/
//    |_| |_| \__,_|| .__/ | .__/  \__, | |____/ | .__/ |_|   |_| \__| \___|
//                  |_|    |_|     |___/         |_|                         
//
//                     HappySprite - We make sprites happy
//
// Copyright (c) 2007 by Tank Monkey Games
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace HappySprite
{
    public class Transform3D
    {
        Vector3 position;
        Rotation rotation;
        Vector3 scale;

        bool isKnownToBeIdentity;

        internal Transform3D()
        {
            rotation = new Rotation(this);
            SetIdentityCore();
        }

        public Vector3 Position
        {
            get 
            {
                UpdateTransform();
                return position; 
            }
            set 
            { 
                position = value;
                isKnownToBeIdentity = false;
                OnTransformChanged();
            }
        }

        public float PositionX
        {
            get 
            {
                UpdateTransform();
                return position.X; 
            }
            set 
            {
                UpdateTransform();
                position.X = value;
                isKnownToBeIdentity = false;
                OnTransformChanged();
            }
        }

        public float PositionY
        {
            get 
            {
                UpdateTransform();
                return position.Y; 
            }
            set 
            {
                UpdateTransform();
                position.Y = value;
                isKnownToBeIdentity = false;
                OnTransformChanged();
            }
        }

        public float PositionZ
        {
            get 
            {
                UpdateTransform();
                return position.Z; 
            }
            set 
            {
                UpdateTransform();
                position.Z = value;
                isKnownToBeIdentity = false;
                OnTransformChanged();
            }
        }

        public Rotation Rotation
        {
            get 
            {
                UpdateTransform();
                return rotation; 
            }
            set 
            {
                rotation.SetRotation(value.Quaternion);
                OnRotationChanged();
            }
        }

        public Vector3 Scale
        {
            get 
            {
                UpdateTransform();
                return scale; 
            }
            set 
            { 
                scale = value;
                isKnownToBeIdentity = false;
                OnTransformChanged();
            }
        }

        public float ScaleX
        {
            get 
            {
                UpdateTransform();
                return scale.X; 
            }
            set 
            {
                UpdateTransform();
                scale.X = value;
                isKnownToBeIdentity = false;
                OnTransformChanged();
            }
        }

        public float ScaleY
        {
            get 
            {
                UpdateTransform();
                return scale.Y; 
            }
            set 
            {
                UpdateTransform();
                scale.Y = value;
                isKnownToBeIdentity = false;
                OnTransformChanged();
            }
        }

        public float ScaleZ
        {
            get 
            {
                UpdateTransform();
                return scale.Z; 
            }
            set 
            {
                UpdateTransform();
                scale.Z = value;
                isKnownToBeIdentity = false;
                OnTransformChanged();
            }
        }

        public virtual void CopyFrom(Transform3D transform)
        {
            CopyFromCore(transform);
        }

        public void SetIdentity()
        {
            SetIdentityCore();
            OnTransformChanged();
        }

        internal void SetIdentityCore()
        {
            position = Vector3.Zero;
            rotation.SetIdentityCore();
            scale = Vector3.One;
            isKnownToBeIdentity = true;
        }

        internal void CopyFromCore(Transform3D transform)
        {
            if (transform.IsKnownToBeIdentity)
            {
                SetIdentityCore();
            }
            else
            {
                SetPosition(transform.position);
                rotation.SetRotation(transform.rotation.Quaternion);
                SetScale(transform.scale);
                isKnownToBeIdentity = false;
            }
        }

        internal void SetPosition(Vector3 position)
        {
            this.position = position;
        }

        internal void SetRotation(Quaternion quaterion)
        {
            rotation.SetRotation(quaterion);
        }

        internal void SetScale(Vector3 scale)
        {
            this.scale = scale;
        }

        internal bool IsKnownToBeIdentity
        {
            get
            {
                return isKnownToBeIdentity && rotation.IsKnownToBeIdentity();
            }
            set
            {
                isKnownToBeIdentity = false;
            }
        }


        protected virtual void OnTransformChanged()
        {
        }

        internal void OnRotationChanged()
        {
            OnTransformChanged();
        }

        protected virtual void UpdateTransform()
        {
        }

        internal Matrix GetMatrix()
        {
            Matrix m = Matrix.Identity;
            Append(ref m);
            return m;
        }

        internal void Append(ref Matrix matrix)
        {
            UpdateTransform();

            if (IsKnownToBeIdentity)
                return;

            MatrixExtensions.Scale(ref matrix, ref scale);

            if (!rotation.IsKnownToBeIdentity())
                matrix *= Matrix.CreateFromQuaternion(rotation.Quaternion);

            MatrixExtensions.Translate(ref matrix, ref position);
        }
    }
}
